#include "ir-block.h"

typedef struct symbol_ * symbol;
struct symbol_{
    char * name;
    enum{SYM_var,SYM_label,SYM_func} kind;
    Operand a;
    symbol nxt;
};
static symbol local_table[16384];
Operand findInsert(const char * name,int kind){
    if(!name) return NULL;
    if(name[0]=='#'){
        int x;
        sscanf(name+1,"%d",&x);
        return wrap_op1(x,CONSTANT);
    }
    static int local_num[3]={};
    int h=hash(name);
    for(symbol now=local_table[h];now;now=now->nxt) 
    if(now->kind==kind&&strcmp(name,now->name)==0) return now->a;
    symbol temp=malloc(sizeof(struct symbol_));
    temp->name=str_maker("%s",name);temp->kind=kind;
    temp->nxt=local_table[h];local_table[h]=temp;
    int num=++local_num[kind];
    if(kind==SYM_var) temp->a=wrap_op1(num,VARIABLE);
    else if(kind==SYM_label) temp->a=wrap_op1(num,LABEL);
    else temp->a=wrap_op2(temp->name,NAME);
    totalVar=local_num[SYM_var];
    return temp->a;
}

static void parse_IF(const char * x,const char * relop,const char * y,const char * z){
    int op;
    if(strcmp(relop,">=")) op=GE;
    else if(strcmp(relop,"<=")) op=LE;
    else if(strcmp(relop,">")) op=GT;
    else if(strcmp(relop,"<")) op=LT;
    else if(strcmp(relop,"==")) op=EQ;
    else if(strcmp(relop,"!=")) op=NE;
    Operand xx=findInsert(x,SYM_var),yy=findInsert(y,SYM_var),zz=findInsert(z,SYM_label);
    op_pb(OP_IF,xx,yy,zz,op);
}

static char buf[4096],*args[7];
int cnt=0;
static void split(){
    args[0]=strtok(buf," \t\n");
    cnt=1;
    while((args[cnt]=strtok(NULL," \t\n"))!=NULL) cnt++;
    for(;cnt<7;cnt++) args[cnt]=NULL;
}
static void parse(){
    split();
    const char * first_arg=args[0];
    if(strcmp(first_arg,"LABEL")==0) 
        op_pb(LABL,findInsert(args[1],SYM_label),NULL,NULL,0);
    else if(strcmp(first_arg,"FUNCTION")==0)
        op_pb(FUNC,findInsert(args[1],SYM_func),NULL,NULL,0);
    else if(strcmp(first_arg,"GOTO")==0)
        op_pb(GOTO,findInsert(args[1],SYM_label),NULL,NULL,0);
    else if(strcmp(first_arg,"IF")==0)
        parse_IF(args[1],args[2],args[3],args[5]);
    else if(strcmp(first_arg,"RETURN")==0)
        op_pb(RET,findInsert(args[1],SYM_var),NULL,NULL,0);
    else if(strcmp(first_arg,"DEC")==0)
        op_pb(DEC,findInsert(args[1],SYM_var),NULL,NULL,0);
    else if(strcmp(first_arg,"ARG")==0)
        op_pb(ARG,findInsert(args[1],SYM_var),NULL,NULL,0);
    else if(strcmp(first_arg,"PARAM")==0)
        op_pb(PARAM,findInsert(args[1],SYM_var),NULL,NULL,0);
    else if(strcmp(first_arg,"READ")==0)
        op_pb(READ,findInsert(args[1],SYM_var),NULL,NULL,0);
    else if(strcmp(first_arg,"WRITE")==0)
        op_pb(WRITE,findInsert(args[1],SYM_var),NULL,NULL,0);
    else{
        Operand xx,yy,zz;
        extern void op_pb2(int type,Operand x,Operand y,Operand z,int nx,int ny,int nz);
        int nx=0,ny=0,nz=0;
        if(args[0][0]=='*') nx=1,xx=findInsert(args[0]+1,SYM_var);
        else xx=findInsert(args[0],SYM_var);
        if(strcmp(args[2],"CALL")==0) 
            op_pb2(CALL,xx,findInsert(args[3],SYM_func),NULL,nx,ny,nz);
        else{
            if(args[2][0]=='*') ny=1,yy=findInsert(args[2]+1,SYM_var);
            else if(args[2][0]=='&') ny=2,yy=findInsert(args[2]+1,SYM_var);
            else yy=findInsert(args[2],SYM_var);
            if(!args[3]) op_pb2(ASSIGN,xx,yy,NULL,nx,ny,nz);
            else{
                int op;
                if(strcmp(args[3],"+")==0) op=ADD;
                else if(strcmp(args[3],"-")==0) op=SUB;
                else if(strcmp(args[3],"*")==0) op=MUL;
                else op=OP_DIV;  
                if(args[4][0]=='*') nz=1,zz=findInsert(args[4]+1,SYM_var);
                else if(args[4][0]=='&') nz=2,zz=findInsert(args[4]+1,SYM_var);
                else zz=findInsert(args[4],SYM_var);
                op_pb2(op,xx,yy,zz,nx,ny,nz);
            }
        }
    }
}

void read_ir(FILE * f){
    while(fgets(buf,4096,f)!=NULL) parse();
    fclose(f);
}
